#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Collections.ObjectModel;
using UTable.Input.MultiTouch;

namespace UTable.Input
{
    /// <summary>
    /// Only used in OpticalProvider.cs, but useless now.
    /// </summary>
	public class NetClient
	{
		public NetClient()
		{
			Thread netClientThread = new Thread(new ThreadStart(this.ThreadProc));
			netClientThread.Start();
		}

		public void ThreadProc()
		{
			if (ConnectToServer())
			{
				if (OnNetConnectSucceed != null)
					OnNetConnectSucceed(this, EventArgs.Empty);
			}
			else
			{
				if (OnNetConnectFail != null)
					OnNetConnectFail(this, EventArgs.Empty);
			}
			while (connected)
			{
				int msgCount = 0;
				Collection<FingerEventArgs> msgCollection = new Collection<FingerEventArgs>();
				// First receive the number of events that will be sent
				if (!ReceiveData(buffer, 4))
				{
					OnNetConnectAbort(this, EventArgs.Empty);
					connected = false;
					client.Close();
					return;
				}
				msgCount = ByteArrayToInteger(buffer, 0);

				// Then receive the finger events
				for (int i=0; i<msgCount; i++)
				{
					if (!ReceiveData(buffer, 4*4))
					{
						OnNetConnectAbort(this, EventArgs.Empty);
						connected = false;
						client.Close();
						return;
					}
					FingerEventArgs msg = new FingerEventArgs();
                    
					//TODO
					// Set the user ID
					// now everyone is super user
                    msg.UserID = -1;
					msg.FingerID = ByteArrayToInteger(buffer, 0);
					msg.EventType = (FingerEventType)ByteArrayToInteger(buffer, 4);
					msg.Position.X = ByteArrayToInteger(buffer, 8);
					msg.Position.Y = ByteArrayToInteger(buffer, 12);
					msgCollection.Add(msg);
				}
				OnMultiTouch(new MultiTouchEventArgs(msgCollection, InputEventArgs.GetTimestamp()));
			}
		}

		int ByteArrayToInteger(Byte[] buf, int offset)
		{
			int result = 0;
			for (int i=0; i<4; i++)
			{
				result = result << 8;
				result += buf[offset + 3 - i];
			}
			return result;
		}

		#region TCP Communication Methods
		public const int BUFFER_LENGTH = 1024;
		bool connected;
		Socket client;
		Byte[] buffer = new byte[BUFFER_LENGTH];
		string _serverIP;
		int _port = 5000;

		bool ConnectToServer()
		{
			// Now use the local IP as the server IP
			string strHostName = Dns.GetHostName();
			IPAddress[] addressList = Dns.GetHostEntry(strHostName).AddressList;
			_serverIP = addressList[0].ToString();

			client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			IPEndPoint ie = new IPEndPoint(IPAddress.Parse(_serverIP), _port);
			try
			{
				client.Connect(ie);
			}
			catch (SocketException e)
			{
                Console.WriteLine(e.StackTrace);
				connected = false;
				return false;
			}
			connected = true;
			return true;
		}

		void CloseConnect()
		{
			client.Close();
			connected = false;
		}

		bool SendData(Byte[] buf, int length)
		{
			int ret;
			int index = 0;

			try
			{
				for (; ; )
				{
					ret = client.Send(buf, index, length, SocketFlags.None);
					if (ret == 0)
					{
						break;
					}
					if (ret < 0)
					{
						return false;
					}
					index += ret;
					length -= ret;
				}
			}
			catch (System.Exception e)
			{
                Console.WriteLine(e.StackTrace);
				return false;
			}

			return true;
		}

		bool ReceiveData(Byte[] buf, int length)
		{
			try
			{
				if (client.Receive(buf, length, SocketFlags.None) < 0)
					return false;
			}
			catch (System.Exception e)
			{
                Console.WriteLine(e.StackTrace);
				return false;
			}

			return true;
		}

		#endregion

		#region Events 
		public event EventHandler OnNetConnectSucceed;
		public event EventHandler OnNetConnectFail;
		public event EventHandler OnNetConnectAbort;
		public event MultiTouchEventHandler OnMultiTouch;

		public delegate void MultiTouchEventHandler(MultiTouchEventArgs args);
		#endregion
	}
}
